import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import javax.swing.Timer;

public class Client {
    // 设置窗口大小
    public static int WIN_SIZE = 5;
    // 设置窗口起始位置
    public static int start = 0;
    // 设置窗口结束位置
    public static int end = start + WIN_SIZE - 1;
    // 设置待发送分组个数
    public static int dataCount = 8;

    public static void main(String[] args) throws Exception {
        // 设置服务端IP地址
        InetAddress serverAddress = InetAddress.getByName("localhost");
        // 设置服务端端口号
        int serverPort = 8888;
        // 创建UDP协议的Socket套接字用于接收和发送分组
        DatagramSocket clientSocket = new DatagramSocket(9999);
        byte[] sendData;
        Timer[] timers = new Timer[100];

        // 发送分组
        for (int i = start; i <= end; i++) {
            // 封装分组
            sendData = (i + "seq").getBytes(StandardCharsets.UTF_8);
            DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
            clientSocket.send(sendPacket);
            // 设置定时器
            timers[i] = new Timer(3000, new DelayActionListener(clientSocket, i, timers));
            // 启动定时器
            timers[i].start();
            System.out.println("客户端发送分组 " + i);
        }

        // 接收ack
        while (true) {
            byte[] receiveData = new byte[100];
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            // 客户端接收分组
            clientSocket.receive(receivePacket);
            // 获取ack号
            int ack_seq = new String(receivePacket.getData()).charAt(3) - '0';
            System.out.println("客户端接收 ack=" + ack_seq);
            // 停止定时器
            timers[ack_seq].stop();
            // 如果ack序号是窗口起始位置，则窗口向后移动并继续新分组
            if (ack_seq == start) {
                start++;
                end++;
                if (end > dataCount - 1) {
                    end = dataCount - 1;
                }
                sendData = (end + "seq").getBytes(StandardCharsets.UTF_8);
                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
                clientSocket.send(sendPacket);
                // 设置定时器
                timers[end] = new Timer(3000, new DelayActionListener(clientSocket, start, timers));
                // 启动定时器
                timers[end].start();
                System.out.println("客户端发送分组 " + end);
            }
            if (ack_seq == dataCount - 1) {
                System.out.println("全部分组均已被成功发送！");
                return;
            }
        }
    }
}

class DelayActionListener implements ActionListener {
    DatagramSocket clientSocket;
    int end_ack;
    Timer[] timers;

    public DelayActionListener(DatagramSocket clientSocket, int end_ack, Timer[] timers) {
        this.clientSocket = clientSocket;
        this.end_ack = end_ack;
        this.timers = timers;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        int end = Client.end;
        System.out.println("客户端准备重传数据 " + end_ack + "--" + end);
        for (int i = end_ack; i <= end; i++) {
            byte[] sendData;
            InetAddress serverAddress = null;
            try {
                serverAddress = InetAddress.getByName("localhost");
                int serverPort = 8888;
                sendData = (i + "seq").getBytes();
                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
                clientSocket.send(sendPacket);
                System.out.println("**客户端发送分组 " + i);
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            timers[i].stop();
            timers[i].start();
        }
    }
}
